/*************************************************************************
 *
 * obsluha 1-wire sbernice
 * 
 * (c) Ales Povalac, alpov@alpov.net, http://www.alpov.net/
 * 
 *************************************************************************
 *
 * $Id: 1wire.c 60 2006-07-27 12:50:53Z alpov $
 * 
 *************************************************************************/         

#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <util/crc16.h>
#include "../avrx/avrx.h"
#include "../avrx/AvrXFifo.h"
#include "1wire.h"


/******************************************************************
 * LOW LEVEL funkce pro 1-wire sbernici
 ******************************************************************/ 

static unsigned char OW_LastDevice = 0;
static unsigned char OW_LastDiscrepancy = 0;
static unsigned char OW_LastFamilyDiscrepancy = 0;

unsigned char OWReset(void)
{
    unsigned char result;

    DELAY_G;
    BeginCritical();
    OWIREOUTPORT &= ~(_BV(OWIREOUTPIN));
    OWIREDDR |= _BV(OWIREOUTPIN);
    DELAY_H;
    OWIREDDR &= ~(_BV(OWIREOUTPIN));
    DELAY_I;
    result = OWIREINPORT & _BV(OWIREINPIN) ? 1 : 0;
    EndCritical();
    DELAY_J;
    return result;
}

void OWWriteBit(unsigned char bit)
{
    BeginCritical();
    if (bit) {
        OWIREOUTPORT &= ~(_BV(OWIREOUTPIN));
        OWIREDDR |= _BV(OWIREOUTPIN);
        DELAY_A;
        OWIREDDR &= ~(_BV(OWIREOUTPIN));
        DELAY_B;
    } else {
        OWIREOUTPORT &= ~(_BV(OWIREOUTPIN));
        OWIREDDR |= _BV(OWIREOUTPIN);
        DELAY_C;
        OWIREDDR &= ~(_BV(OWIREOUTPIN));
        DELAY_D;
    }
    EndCritical();
}

unsigned char OWReadBit(void)
{
    unsigned char result;

    BeginCritical();
    OWIREOUTPORT &= ~(_BV(OWIREOUTPIN));
    OWIREDDR |= _BV(OWIREOUTPIN);
    DELAY_A;
    OWIREDDR &= ~(_BV(OWIREOUTPIN));
    DELAY_E;
    result = OWIREINPORT & _BV(OWIREINPIN) ? 1 : 0;
    EndCritical();
    DELAY_F;
    return result;
}

void OWStrongPullup(unsigned char on_off)
{
    if (on_off) {
        OWIREOUTPORT |= _BV(OWIREOUTPIN);
        OWIREDDR |= _BV(OWIREOUTPIN);
    } else {
        OWIREOUTPORT &= ~(_BV(OWIREOUTPIN));
        OWIREDDR &= ~(_BV(OWIREOUTPIN));
    }
}

void OWWriteByte(unsigned char data)
{
    unsigned char i;
    
    for (i = 0; i < 8; i++) {
        OWWriteBit(data & 0x01);
        data >>= 1;
    }
}

unsigned char OWReadByte(void)
{
    int i, result = 0;
    
    for (i = 0; i < 8; i++) {
        result >>= 1;
        if (OWReadBit())
            result |= 0x80;
    }
    return result;
}

void OWSendCmd(unsigned char *ROM, unsigned char cmd)
{
    unsigned char i;

    OWReset();
    if (ROM == NULL) {
        OWWriteByte(OW_SKIP_ROM_CMD);
    } else {
        OWWriteByte(OW_MATCH_ROM_CMD);
        for (i = 0; i < 8; i++)
            OWWriteByte(ROM[i]);
    }
    OWWriteByte(cmd);
}

char OWFirst(unsigned char *ROM)
{
    /* Reset state */
    OW_LastDiscrepancy = 0;
    OW_LastDevice = 0;
    OW_LastFamilyDiscrepancy = 0;
    
    /* Go looking */
    return OWNext(ROM);
}

char OWNext(unsigned char *ROM)
{
    unsigned char bit_test, search_direction, bit_number;
    unsigned char last_zero, rom_byte_number, rom_byte_mask;
    unsigned char lastcrc8, crcaccum;
    char next_result;

    /* Init for search */
    bit_number = 1;
    last_zero = 0;
    rom_byte_number = 0;
    rom_byte_mask = 1;
    next_result = OW_NOMODULES;
    lastcrc8 = 0;
    crcaccum = 0;
    
    /* if the last call was not the last one */
    if (!OW_LastDevice) {
        /* reset the 1-wire if there are no parts on 1-wire, return 0 */
        if (OWReset()) {
            /* reset the search */
            OW_LastDiscrepancy = 0;
            OW_LastFamilyDiscrepancy = 0;
            return OW_NOPRESENCE;
        }
        
        OWWriteByte(OW_SEARCH_ROM_CMD); /* issue the search command */
        
        /* pause before beginning the search - removed */
        /* loop to do the search */
        do {
            /* read a bit and its compliment */
            bit_test = OWReadBit() << 1;
            bit_test |= OWReadBit();
            
            /* check for no devices on 1-wire */
            if (bit_test == 3) {
                return(OW_BADWIRE);
            } else {
                /* all devices coupled have 0 or 1 */
                if (bit_test > 0)
                    search_direction = !(bit_test & 0x01);  /* bit write value for search */
                else {
                    /* if this discrepancy is before the Last Discrepancy
                     * on a previous OWNext then pick the same as last time */
                    if (bit_number < OW_LastDiscrepancy)
                        search_direction = ((ROM[rom_byte_number] & rom_byte_mask) > 0);
                    else
                        /* if equal to last pick 1, if not then pick 0 */
                        search_direction = (bit_number == OW_LastDiscrepancy);
                    
                    /* if 0 was picked then record its position in LastZero */
                    if (search_direction == 0) {
                        last_zero = bit_number;
                        /* check for Last discrepancy in family */
                        if (last_zero < 9)
                            OW_LastFamilyDiscrepancy = last_zero;
                    }
                }
                
                /* set or clear the bit in the ROM byte rom_byte_number
                 * with mask rom_byte_mask */
                if (search_direction == 1)
                    ROM[rom_byte_number] |= rom_byte_mask;
                else
                    ROM[rom_byte_number] &= ~rom_byte_mask;
                
                /* serial number search direction write bit */
                OWWriteBit(search_direction);
                
                /* increment the byte counter bit_number
                 * and shift the mask rom_byte_mask */
                bit_number++;
                rom_byte_mask <<= 1;
                
                /* if the mask is 0 then go to new ROM byte rom_byte_number
                 * and reset mask */
                if (rom_byte_mask == 0) {
                    OWCRC(ROM[rom_byte_number], &crcaccum);  /* accumulate the CRC */
                    lastcrc8 = crcaccum;
                    
                    rom_byte_number++;
                    rom_byte_mask = 1;
                }
            }
        } while (rom_byte_number < 8);  /* loop until through all ROM bytes 0-7 */
        
        /* if the search was successful then */
        if (!(bit_number < 65) || lastcrc8) {
            if (lastcrc8) {
                next_result = OW_BADCRC;
            } else {
                /*  search successful so set LastDiscrepancy,LastDevice,next_result */
                OW_LastDiscrepancy = last_zero;
                OW_LastDevice = (OW_LastDiscrepancy == 0);
                next_result = OW_FOUND;
            }
        }
    }
    
    /* if no device found then reset counters so next 'next' will be like a first */
    if (next_result != OW_FOUND || ROM[0] == 0) {
        OW_LastDiscrepancy = 0;
        OW_LastDevice = 0;
        OW_LastFamilyDiscrepancy = 0;
    }
    
    if (next_result == OW_FOUND && ROM[0] == 0x00)
        next_result = OW_BADWIRE;
    
    return next_result;
}

void OWCRC(unsigned char x, unsigned char *crc)
{
    *crc = _crc_ibutton_update(*crc, x);
}

/******************************************************************
 * HIGH LEVEL funkce pro 1-wire sbernici
 ******************************************************************/ 

void OWConvertAll(unsigned char wait)
{
    TimerControlBlock timer;
    
	timer.semaphore = SEM_PEND;
    OWSendCmd(NULL, OW_CONVERT_T_CMD);
    if (wait) {
        OWStrongPullup(1);
        AvrXDelay(&timer, CONVERT_T_DELAY);
        OWStrongPullup(0);
    }
}

unsigned char OWReadTemperature(unsigned char *ROM, int *result)
{       
    unsigned char i, crc = 0, buf[8];
    
    OWSendCmd(ROM, OW_RD_SCR_CMD);
    for (i = 0; i < 8; i++) {
        buf[i] = OWReadByte();
        OWCRC(buf[i], &crc);
    }
    if (crc != OWReadByte())
        return 0;

    switch (ROM[0]) {
        case 0x10: {    // ds18s20
            *result = (signed char)((buf[1] & 0x80) | (buf[0] >> 1)) * 100 + 75 - buf[6] * 100 / 16;
            break;
        }
        case 0x28: {    // ds18b20
            *result = (signed char)((buf[1] << 4) | (buf[0] >> 4)) * 100 + (buf[0] & 0x0F) * 100 / 16;
            break;
        }
        default:
            return 0;
    }
    return 1;
}

